package com.content.scaffold.common.config.obs;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import com.aliyuncs.utils.StringUtils;
import com.content.scaffold.common.exception.BaseException;
import com.content.scaffold.common.web.ResponseCodeEnum;
import com.content.scaffold.model.dto.file.FileInfo;
import com.notary.ossservice.FileOperateTemplate;
import com.notary.ossservice.dto.FileOperation;
import com.notary.ossservice.dto.TempUpload;
import com.notary.ossservice.exception.FileOperateException;
;
import com.notarycloud.client.NotaryCloudClient;
import com.notarycloud.client.NotaryCloudFilePair;
import com.notarycloud.client.NotaryCloudObjectMap;
import com.notarycloud.client.NotaryCloudResponse;
import com.obs.services.model.CopyObjectResult;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;

import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;


import java.io.*;
import java.net.URLEncoder;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * @author xiaojl
 */
@Slf4j
@Component
public class FileOperateHelper {
    public static final String ZT_UPLOAD = "/api/file/fileService/uploadFile";
    public static final String ZT_DOWNLOAD = "/api/file/fileService/downloadFile";
    public static final String FAMILY_UPLOAD = "/openapi/file/uploadFile";
    public static final String ERROR_MSG = "文件上传失败";


    private FileOperateHelper() {
        // throw new IllegalStateException("Utility class");
    }

    @Resource
    public FileOperateTemplate fileOperateTemplate;

    private static FileOperateHelper fileOperateHelper;

    @PostConstruct
    public void init() {
        log.debug("fileOperateHelper 初始化");
        fileOperateHelper = this;
        fileOperateHelper.fileOperateTemplate = this.fileOperateTemplate;
    }

    /**
     * 上传oss文件至中台
     *
     * @param url               文件地址
     * @param notaryCloudClient 中台服务客户端
     * @return com.notarycloud.client.NotaryCloudResponse 中台服务响应
     * @date 2023/2/27 14:08
     */
    public static NotaryCloudResponse upload2CenterResponse(String url, NotaryCloudClient notaryCloudClient) {
        return upload2CenterResponse(url, null, notaryCloudClient);
    }

    public static NotaryCloudResponse upload2CenterResponse(String url, NotaryCloudClient notaryCloudClient, byte[] bytes) {
        return upload2CenterResponse(url, bytes, null, notaryCloudClient);
    }


    /**
     * 上传oss文件至中台
     *
     * @param url               文件地址
     * @param notaryCloudClient 中台服务客户端
     * @param isEncrypt         是否加密
     * @return com.notarycloud.client.NotaryCloudResponse 中台服务响应
     * @date 2023/3/9 14:08
     */
    public static NotaryCloudResponse upload2CenterResponse(String url, Boolean isEncrypt, NotaryCloudClient notaryCloudClient) {
        return fileOperateHelper.fileOperateTemplate.of(url).setWhetherEncrypt(Boolean.TRUE.equals(isEncrypt)).inputStreamFun(inputStream -> {
            try {
                NotaryCloudFilePair filePair = new NotaryCloudFilePair();
                filePair.setFileName(FilenameUtils.getName(url));
                filePair.setInputStream(inputStream);
                return notaryCloudClient.newFileRequest().setPath(ZT_UPLOAD).upload(filePair);
            } catch (IOException e) {
                return null;
            }
        });
    }


    public static NotaryCloudResponse upload2CenterResponse(String fileName, byte[] bytes, Boolean isEncrypt, NotaryCloudClient notaryCloudClient) {

        try {
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
            NotaryCloudFilePair filePair = new NotaryCloudFilePair();
            filePair.setFileName(FilenameUtils.getName(fileName));
            filePair.setInputStream(byteArrayInputStream);
            return notaryCloudClient.newFileRequest().setPath(ZT_UPLOAD).upload(filePair);
        } catch (IOException e) {
            throw new BaseException(ResponseCodeEnum.ERROR, "上传中台失败");
        }
    }


    /**
     * 下载oss文件上传至中台，返回中台文件id
     *
     * @param url               文件地址
     * @param notaryCloudClient 中台服务客户端
     * @return java.lang.String 中台文件id
     * @date 2023/2/27 14:09
     */
    public static String upload2Center(String url, NotaryCloudClient notaryCloudClient) {
        return upload2Center(url, null, notaryCloudClient);
    }

    /**
     * 下载oss文件上传至中台，返回中台文件id
     *
     * @param url               文件地址
     * @param notaryCloudClient 中台服务客户端
     * @return java.lang.String 中台文件id
     */
    public static String upload2Center(String url, Boolean isEncrypt, NotaryCloudClient notaryCloudClient) {
        return Optional.ofNullable(upload2CenterResponse(url, isEncrypt, notaryCloudClient))
                .filter(NotaryCloudResponse::isOK)
                .map(r -> r.getDataAs(String.class))
                .orElseThrow(() -> new BaseException(ResponseCodeEnum.ERROR, ERROR_MSG));
    }


    public static String upload2CenterByByte(String url, byte[] bytes, NotaryCloudClient notaryCloudClient) {
        return Optional.ofNullable(upload2CenterResponse(url, notaryCloudClient, bytes))
                .filter(NotaryCloudResponse::isOK)
                .map(r -> r.getDataAs(String.class))
                .orElseThrow(() -> new BaseException(ResponseCodeEnum.ERROR, ERROR_MSG));
    }


    /**
     * 下载中台文件上传至oss，返回文件url
     *
     * @param fileName          文件名（包含扩展名）
     * @param centerFileId      中台文件id
     * @param notaryCloudClient 中台服务客户端
     * @return java.lang.String
     * @date 2023/2/27 14:22
     */
    public static FileInfo downloadFromCenter(String fileName, String centerFileId, NotaryCloudClient notaryCloudClient) {
        return downloadFromCenter(fileName, centerFileId, null, notaryCloudClient);
    }

    /**
     * 下载中台文件上传至oss，返回文件url
     *
     * @param fileName          文件名（包含扩展名）
     * @param centerFileId      中台文件id
     * @param notaryCloudClient 中台服务客户端
     * @param notaryOfficeId    公证处id
     * @return java.lang.String
     * @date 2023/2/27 14:22
     */
    public static FileInfo downloadFromCenter(String fileName, String centerFileId, String notaryOfficeId, NotaryCloudClient notaryCloudClient) {
        try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
            NotaryCloudObjectMap params = new NotaryCloudObjectMap().append("fileId", centerFileId);
            notaryCloudClient.newFileRequest().setPath(ZT_DOWNLOAD).setData(params).download(byteArrayOutputStream);
            byte[] bytes = byteArrayOutputStream.toByteArray();
            String url;
            if (StringUtils.isEmpty(notaryOfficeId)) {
                url = fileOperateHelper.fileOperateTemplate.of((new ByteArrayInputStream(bytes))).setFileName(fileName).upload();
            } else {
                url = fileOperateHelper.fileOperateTemplate.of((new ByteArrayInputStream(bytes))).setNotaryOfficeId(notaryOfficeId).setFileName(fileName).upload();
            }
            return FileInfo.builder().fileSize(Convert.toLong(bytes.length)).obsObjectName(url).fileName(fileName).fileType(FileUtil.getMimeType(fileName)).build();
        } catch (IOException e) {
            log.error("下载中台文件失败, fileId:{}", centerFileId, e);
            throw new BaseException(ResponseCodeEnum.ERROR, "下载文件失败");
        }
    }


    /**
     * 下载文件
     *
     * @param response  响应
     * @param consumer  传输数据
     * @param fileName  文件名
     * @param mediaType 内容类型
     * @return void
     * @date 2023/2/28 15:52
     */
    public static void download(HttpServletResponse response, Consumer<OutputStream> consumer, String fileName, String mediaType) {
        OutputStream os = null;
        try {
            response.reset();
            Optional.ofNullable(mediaType).ifPresent(response::setContentType);
            if (StringUtils.isEmpty(fileName)) {
                response.setHeader("Content-Disposition",
                        "attachment; filename=" + URLEncoder.encode(fileName, "utf-8"));
            }
            os = response.getOutputStream();
            consumer.accept(os);
        } catch (Exception e) {
            log.error("下载失败", e);
        } finally {
            IOUtils.closeQuietly(os);
        }
    }

    /**
     * 获取文件操作器
     *
     * @param url 路径
     * @return com.notary.ossservice.dto.FileOperation
     * @date 2023/3/16 10:31
     */
    public static FileOperation getOperation(String url) {
        return fileOperateHelper.fileOperateTemplate.of(url);
    }

    /**
     * 复制文件
     *
     * @param srcUrl 源文件
     * @return java.lang.String 复制后文件地址
     * @date 2023/3/1 13:53
     */
    public static String copyFile(String srcUrl) {
        return fileOperateHelper.fileOperateTemplate.of(srcUrl).inputStreamFun(inputStream ->
                fileOperateHelper.fileOperateTemplate.of(inputStream)
                        .setFileName(FilenameUtils.getName(srcUrl))
                        .upload());
    }


    /**
     * 复制文件
     *
     * @param srcUrl        源文件
     * @param isSrcEncrypt  源文件是否加密
     * @param isDestEncrypt 复制后文件是否加密
     * @return java.lang.String
     * @date 2023/3/17 17:55
     */
    public static String copyFile(String srcUrl, boolean isSrcEncrypt, boolean isDestEncrypt) {
        return fileOperateHelper.fileOperateTemplate.of(srcUrl).inputStreamFun(inputStream ->
                fileOperateHelper.fileOperateTemplate.of(inputStream)
                        .setFileName(FilenameUtils.getName(srcUrl))
                        .upload());
    }


    /**
     * 上传输入流至目标地址
     *
     * @param inputStream 输入流
     * @param fileName    文件名 包含扩展名
     * @param isEncrypt   是否加密
     * @param uploadUrl   目标地址
     * @return java.lang.String
     * @date 2023/3/3 14:43
     */
    public static String upload(InputStream inputStream, String fileName, boolean isEncrypt, String uploadUrl) {
        return fileOperateHelper.fileOperateTemplate.of(inputStream)
                .setFileName(fileName).setUploadUrl(uploadUrl).setWhetherEncrypt(isEncrypt).upload();
    }

    /**
     * 上传输入流至目标地址
     *
     * @param inputStream 输入流
     * @param fileName    文件名 包含扩展名
     * @param isEncrypt   是否加密
     * @return java.lang.String
     * @date 2023/3/3 16:35
     */
    public static String upload(InputStream inputStream, String fileName, boolean isEncrypt) {
        return upload(inputStream, fileName, isEncrypt, null);
    }

    /**
     * 上传输入流至目标地址
     *
     * @param inputStream 输入流
     * @param fileName    文件名 包含扩展名
     * @return java.lang.String
     * @date 2023/3/3 16:36
     */
    public static String upload(InputStream inputStream, String fileName) {
        return upload(inputStream, fileName, false, null);
    }


    /**
     * 上传字节数组至目标地址
     *
     * @param bytes     字节数组
     * @param fileName  文件名 包含扩展名
     * @param isEncrypt 是否加密
     * @param uploadUrl 目标地址
     * @return java.lang.String
     * @date 2023/3/3 14:44
     */
    public static String upload(byte[] bytes, String fileName, boolean isEncrypt, String uploadUrl) {
        return fileOperateHelper.fileOperateTemplate.of(bytes)
                .setFileName(fileName).setUploadUrl(uploadUrl).setWhetherEncrypt(isEncrypt).upload();
    }

    public static String uploadWithOutOfficeId(byte[] bytes, String fileName, boolean isEncrypt, String notaryOfficeId) {
        return fileOperateHelper.fileOperateTemplate.of(bytes)
                .setFileName(fileName).setNotaryOfficeId(notaryOfficeId).setWhetherEncrypt(isEncrypt).upload();
    }

    /**
     * 上传字节数组至目标地址
     *
     * @param bytes     字节数组
     * @param fileName  文件名 包含扩展名
     * @param isEncrypt 是否加密
     * @return java.lang.String
     * @date 2023/3/3 16:38
     */
    public static String upload(byte[] bytes, String fileName, boolean isEncrypt) {
        return upload(bytes, fileName, isEncrypt, null);
    }


    /**
     * 上传字节数组至目标地址
     *
     * @param bytes    字节数组
     * @param fileName 文件名 包含扩展名
     * @return java.lang.String
     * @date 2023/3/3 16:39
     */
    public static String upload(byte[] bytes, String fileName) {
        return upload(bytes, fileName, false, null);
    }

    /**
     * 上传文件至目标地址
     *
     * @param file      文件
     * @param isEncrypt 是否加密
     * @param uploadUrl 目标地址
     * @return java.lang.String
     * @date 2023/3/3 16:31
     */
    public static String upload(MultipartFile file, boolean isEncrypt, String uploadUrl) {
        return fileOperateHelper.fileOperateTemplate.of(file).setUploadUrl(uploadUrl).setWhetherEncrypt(isEncrypt).upload();
    }

    /**
     * 上传文件至目标地址
     *
     * @param file      文件
     * @param isEncrypt 是否加密
     * @return java.lang.String
     * @date 2023/3/3 16:41
     */
    public static String upload(MultipartFile file, boolean isEncrypt) {
        return fileOperateHelper.fileOperateTemplate.of(file).setWhetherEncrypt(isEncrypt).upload();
    }

    /**
     * 上传文件至目标地址
     *
     * @param file 文件
     * @return java.lang.String
     * @date 2023/3/3 16:41
     */
    public static String upload(MultipartFile file) {
        return upload(file, false);
    }


    /**
     * 下载消费输入流并返回信息
     *
     * @param url       路径
     * @param fun       消费方法
     * @param isEncrypt 是否加密
     * @return T
     * @date 2023/3/7 11:00
     */
    public static <T> T downloadFun(String url, Function<InputStream, T> fun, boolean isEncrypt) {
        return fileOperateHelper.fileOperateTemplate.of(url).setWhetherEncrypt(isEncrypt).inputStreamFun(fun);
    }

    /**
     * 下载消费输入流并返回信息
     *
     * @param url 路径
     * @param fun 消费方法
     * @return T
     * @date 2023/3/7 11:00
     */
    public static <T> T downloadFun(String url, Function<InputStream, T> fun) {
        return downloadFun(url, fun, false);
    }

    /**
     * 下载消费输入流
     *
     * @param url       路径
     * @param consumer  消费方法
     * @param isEncrypt 是否加密
     * @return void
     * @date 2023/3/7 11:01
     */
    public static void download(String url, Consumer<InputStream> consumer, boolean isEncrypt) {
        fileOperateHelper.fileOperateTemplate.of(url).setWhetherEncrypt(isEncrypt).inputStream(consumer);
    }

    /**
     * 下载消费输入流
     *
     * @param url      路径
     * @param consumer 消费方法
     * @return void
     * @date 2023/3/7 11:01
     */
    public static void download(String url, Consumer<InputStream> consumer) {
        download(url, consumer, false);
    }


    /**
     * 下载输出流
     *
     * @param url          路径
     * @param outputStream 输出流
     * @param isEncrypt    是否加密
     * @return void
     * @date 2023/3/7 11:01
     */
    public static void download(String url, OutputStream outputStream, boolean isEncrypt) {
        fileOperateHelper.fileOperateTemplate.of(url).setWhetherEncrypt(isEncrypt).outputStream(outputStream);
    }

    /**
     * 下载输出流
     *
     * @param url          路径
     * @param outputStream 输出流
     * @return void
     * @date 2023/3/7 11:01
     */
    public static void download(String url, OutputStream outputStream) {
        download(url, outputStream, false);
    }

    /**
     * 下载base64编码值
     *
     * @param url       路径
     * @param isEncrypt 是否加密
     * @return java.lang.String
     * @date 2023/3/7 11:02
     */
    public static String download(String url, boolean isEncrypt) {
        return fileOperateHelper.fileOperateTemplate.of(url).setWhetherEncrypt(isEncrypt).base64Str();
    }

    /**
     * 下载base64编码值
     *
     * @param url 路径
     * @return java.lang.String
     * @date 2023/3/7 11:02
     */
    public static String download(String url) {
        return download(url, false);
    }

    /***
     * 判断文件是否存在
     *
     * @param url 路径
     * @return boolean
     *
     * @date 2023/3/7 15:32
     */
    public static boolean exists(String url) {
        return fileOperateHelper.fileOperateTemplate.of(url).exists();
    }


    /**
     * 下载后，转换输出流后上传 (用于根据模板生成)
     *
     * @param url                  路径
     * @param outputStreamConsumer 文件处理消费
     * @return java.lang.String
     * @date 2023/3/7 15:54
     */
    public static String processFile(String url, BiConsumer<InputStream, OutputStream> outputStreamConsumer, boolean isEncrypt, String uploadUrl) {
        return FileOperateHelper.downloadFun(url, is -> processFile(url, os -> outputStreamConsumer.accept(is, os), isEncrypt, uploadUrl));
    }

    public static String processFile(String url, BiConsumer<InputStream, OutputStream> outputStreamConsumer) {
        return processFile(url, outputStreamConsumer, false, null);
    }

    public static String processFile(String url, BiConsumer<InputStream, OutputStream> outputStreamConsumer, Boolean isEncrypt) {
        return processFile(url, outputStreamConsumer, isEncrypt, null);
    }

    /**
     * 本地文件，转换输出流后上传 (用于根据模板生成)
     *
     * @param url                  本地文件路径
     * @param outputStreamConsumer 文件处理消费
     * @return java.lang.String
     * @date 2023/3/8 17:01
     */
    public static String processFile(String url, Consumer<OutputStream> outputStreamConsumer, boolean isEncrypt, String uploadUrl) {
        try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
            outputStreamConsumer.accept(byteArrayOutputStream);
            return FileOperateHelper.upload(byteArrayOutputStream.toByteArray(), FilenameUtils.getName(url), isEncrypt, uploadUrl);
        } catch (Exception e) {
            log.error("文件生成失败，错误：{}", ExceptionUtil.stacktraceToOneLineString(e));
            throw new FileOperateException("文件生成失败，错误：" + ExceptionUtil.stacktraceToOneLineString(e));
        }
    }

    public static String processFile(String url, Consumer<OutputStream> outputStreamConsumer) {
        return processFile(url, outputStreamConsumer, false, null);
    }

    /**
     * 文档转字节数组
     *
     * @return byte[]
     * @date 2023/3/8 18:09
     */
    public static byte[] urlToByteArray(String url) {
        return urlToByteArray(url, false);
    }


    public static byte[] urlToByteArray(String url, Boolean isEncrypt) {
        return fileOperateHelper.fileOperateTemplate.of(url).setWhetherEncrypt(Boolean.TRUE.equals(isEncrypt)).inputStreamFun(inputStream -> {
            try {
                return IoUtil.readBytes(inputStream);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });
    }

    public static void delFileByUrl(String url) {
        if (StringUtils.isEmpty(url)) {
            return;
        }
        fileOperateHelper.fileOperateTemplate.of(url).delete();
    }

    /**
     * 获取文件的临时url
     *
     * @param url 文件url
     * @return String
     */
    public static String getTempFileUrl(String url) {
        if (StringUtils.isEmpty(url)) {
            return null;
        }
        return fileOperateHelper.fileOperateTemplate.of(url).tempFileUrl();
    }

    public static TempUpload getUploadTempFileUrl(String url, Map<String, String> headers) {
        return fileOperateHelper.fileOperateTemplate.of(url).setHeaders(headers).uploadTempFileUrl();
    }

    public static String crateUploadPath(String fileName) {
        return fileOperateHelper.fileOperateTemplate.of().setFileName(fileName).crateUploadPath();
    }

    public static String crateUploadPath(String fileName, String officeId) {
        return fileOperateHelper.fileOperateTemplate.of().setFileName(fileName).setNotaryOfficeId(officeId).crateUploadPath();
    }

    public static CopyObjectResult copyObject(String sourceBucketName, String sourceObjectKey, String destinationObjectKey) {
        return fileOperateHelper.fileOperateTemplate.of().setSourceBucketName(sourceBucketName)
                .setSourceObjectKey(sourceObjectKey).setDestinationObjectKey(destinationObjectKey).copyObject();
    }


    /**
     * 下载后，转换输出流后上传 (用于根据文档生成，下载上传是否加密根据原文件是否加密)
     *
     * @param url                  路径
     * @param outputStreamConsumer 文件处理消费
     * @return java.lang.String
     * @date 2023/3/7 15:54
     */
    public static String processFileByDoc(String url, BiConsumer<InputStream, OutputStream> outputStreamConsumer, boolean isEncrypt, String uploadUrl) {
        return FileOperateHelper.downloadFun(url, is -> processFile(url, os -> outputStreamConsumer.accept(is, os), isEncrypt, uploadUrl), isEncrypt);
    }


}
